import java.io.*;
import java.util.*;

/**
 * Class to write to and read from the database.
 * 
 * @author Richie, Miguel, Carlo, Johnson
 * 
 * @param users 		HashMap<String,User> the database which stores the users
 * @param restaurants 	HashMap<String,Restaurant> the database which stores the restaurants
 *
 */
public class DataBase extends DataBaseFramework {

  private HashMap<String,User> users;
  private HashMap<String,Restaurant> restaurants;

	/**
	 * Class Constructor: When initialized, programmatically create users and restaurants
	 */
  public DataBase() {
    setUsers(populateUsers());
    setRestaurants(populateRestaurants());
  }

  /**
   * Class Constructor:  When initialized, get users and restaurants data
   * 					 from .db files
   * 
   * @param serialize	String to tell constructor how to create database
   */
  public DataBase(String serialize) {
    super();
    restaurants = new HashMap<String,Restaurant>();
    for (String name : getPersistentObjects().keySet()) {
    	restaurants.put(name, (Restaurant) getPersistentObjects().get(name));
    }
    readUserDB();
  }
  
  /**
   * Method to set a new User database
   * 
   * @param u			HashMap<String,User> New User database
   */
  public void setUsers(HashMap<String,User> u) {users = u;}
  
  /**
   * Method to return the current User database
   * @return	HashMap<String,User> the current User database
   */
  public HashMap<String,User> getUsers() {return users;}
  
  /**
   * Method to set a new Restaurant database
   * 
   * @param r			HashMap<String,Restaurant> New Restaurant database
   */
  public void setRestaurants(HashMap<String,Restaurant> r) {
	  HashMap<String,PersistentObject> result = new HashMap<String,PersistentObject>();
	  for (String name : r.keySet()) {
		  result.put(name, r.get(name));
	  }
	  setPersistentObjects(result);
  }
  
  /**
   * Method to return the current Restaurant database
   * 
   * @return	HashMap<String,Restaurant> the current Restaurant database
   */
  public HashMap<String,Restaurant> getRestaurants() {
	  HashMap<String,Restaurant> r = new HashMap<String,Restaurant>();
	  for (String name : getPersistentObjects().keySet()) {
		  r.put(name, (Restaurant) getPersistentObjects().get(name));
	  }
	  return r;
  }

  /**
   * Method to read the file user.db to create the User database
   */
public void readUserDB() {
    try {
      ObjectInputStream stream = new ObjectInputStream(new FileInputStream("user.db"));
      HashMap<String, User> users = (HashMap<String, User>) (stream.readObject());
      stream.close();
      setUsers(users);
    }
    catch (IOException ex) {
			ex.printStackTrace();
    }
    catch (ClassNotFoundException ex) {
			ex.printStackTrace();
		}
  }

  /**
   * Method to write to the file user.db the current User database
   */
  public void writeUserDB() {
    HashMap<String, User> users = getUsers();
    try {
      ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream("user.db"));
      stream.writeObject(users);
      stream.close();
    }
    catch (IOException ex) {
			ex.printStackTrace();
    }
  }


  /**
   * Method to hard code a default User database for testing purposes.
   * 
   * @return 	HashMap<String, User> A default User database
   */
  public HashMap<String, User> populateUsers() {
    HashMap<String, User> users = new HashMap<String, User>();

    // create the users
    User richie = new User("richie", "password", "admin", new Vector<UserPreferences>());
    User miguel = new User("miguel", "password", "member", new Vector<UserPreferences>());
    User carlo = new User("carlo", "password", "admin", new Vector<UserPreferences>());
    User jovan = new User("jovan", "password", "member", new Vector<UserPreferences>());

    // add them to the database
    users.put("richie", richie);
    users.put("miguel", miguel);
    users.put("carlo", carlo);
    users.put("jovan", jovan);

    return users;
  }

  /**
   * Method to hard code a default Restaurant database for testing
   * purposes.
   * 
   * @return	HashMap<String, Restaurant> A default Restaurant database
   */
  public HashMap<String, Restaurant> populateRestaurants() {
    HashMap<String, Restaurant> restaurants = new HashMap<String, Restaurant>();
    Preferences prefs;

    // create McDonald's
    prefs = new Preferences();
    prefs.addCost(Constants.CHEAP);
    prefs.addMeal(Constants.BREAKFAST); prefs.addMeal(Constants.LUNCH);
    prefs.addType(Constants._24HRS); prefs.addType(Constants.DRIVETHRU); prefs.addType(Constants.TAKEOUT);
    prefs.addMood(Constants.CASUAL);
    Restaurant mcdonalds = new Restaurant("McDonald's", "4260 Nobel Dr., San Diego, CA 92122", "92122", Constants.BURGERS, prefs);
    restaurants.put("McDonald's", mcdonalds);

    // create Burger King
    prefs = new Preferences();
    prefs.addCost(Constants.EXPENSIVE);
    prefs.addMeal(Constants.BREAKFAST); prefs.addMeal(Constants.LUNCH);
    prefs.addType(Constants._24HRS); prefs.addType(Constants.DRIVETHRU); prefs.addType(Constants.TAKEOUT);
    prefs.addMood(Constants.CASUAL);
    Restaurant burgerking = new Restaurant("Burger King", "5260 La Jolla Village Dr., San Diego, CA 92122", "92122", Constants.BURGERS, prefs);
    restaurants.put("Burger King", burgerking);   
    
    // create KFC
    prefs = new Preferences();
    prefs.addCost(Constants.AFFORDABLE);
    prefs.addMeal(Constants.BREAKFAST); prefs.addMeal(Constants.LUNCH);
    prefs.addType(Constants.DRIVETHRU); prefs.addType(Constants.TAKEOUT);
    prefs.addMood(Constants.CASUAL);
    Restaurant kfc = new Restaurant("KFC", "4290 Clairemont Mesa Blvd., San Diego, CA 92117", "92117", Constants.CHICKEN, prefs);
    restaurants.put("KFC", kfc);

    // create Dominoes
    prefs = new Preferences();
    prefs.addCost(Constants.AFFORDABLE);
    prefs.addMeal(Constants.BREAKFAST); prefs.addMeal(Constants.LUNCH); prefs.addMeal(Constants.DINNER);
    prefs.addType(Constants._24HRS); prefs.addType(Constants.TAKEOUT);
    prefs.addMood(Constants.BUSINESS);
    Restaurant dominoes = new Restaurant("Dominoes", "3211 Holiday Court, La Jolla, CA 92037", "92037", Constants.PIZZA, prefs);
    restaurants.put("Dominoes", dominoes);

    return restaurants;
  }

  /**
   * Method to get a User object from the User database.
   * 
   * @param uname	String User name to search for
   * @return		null if user is not found and the User object if found.
   */
  public User getUser(String uname) {
    HashMap<String,User> users = getUsers();
    User user = (User)(users.get(uname));
    return user;
  }
  
  /**
   * Method to add an User object to the User database.
   * 
   * @param uname 	String the name of the User.
   * @param user	User the User object
   */
  public void addUser(String uname, User user) {
    users.put(uname, user);
  }

  /**
   * Method to add preferences to the User object.
   * 
   * @param uname	String the name of the User
   * @param prefs	UserPreferences the preferences for the User
   */
  public void addUserPreferences(String uname, UserPreferences prefs) {
	  ((User)(users.get(uname))).addPreferences(prefs);
    users.put(uname, (User)(users.get(uname)));
  }

  /**
   * Method to find a Restaurant object from the Restaurant database.
   * 
   * @param restName	String the name of the Restaurant to find
   * @return 			null if restaurant is not found, the restaurant object if found
   */
  public Restaurant getRestaurant(String restName) {
    return (Restaurant) getObject(restName);
  }

  /**
   * Method to add a new restaurant to the Restaurant database.
   * 
   * @param restName String the restaurant name
   * @param rest	 Restaurant the restaurant object
   */
  public void addRestaurant(String restName, Restaurant rest) {
    add(rest);
  }

  public void deleteRestaurant(String restName) {
    delete(restName);
  }

  /**
   * Method to return a list of restaurant names
   * 
   */
  public Vector<String> getRestaurantsByName() {
	  Vector<String> results = new Vector<String>();
	  for (String name : getPersistentObjects().keySet()) {
		  results.add(name);
	  }
	  return results;
  }

  /**
   * Method to return a list of restaurants that have the desired food type
   * 
   * @param foodType	String the desired food type
   * @param zipCode		String the zip code 
   * 					the desired food type
   */
  public Vector<String> getRestaurantsByFoodType(String foodType, String zipCode) {
    Vector<String> results = new Vector<String>();
    HashMap<String,Restaurant> restaurants = getRestaurants();

    Iterator<String> iterator = (restaurants.keySet()).iterator();
    while(iterator.hasNext()) {
      String restname = iterator.next(); // get key
      Restaurant r = (Restaurant)(restaurants.get(restname)); // get restaurant
      if (r.getFoodType().equals(foodType) && r.getZipCode().equals(zipCode)){
        results.addElement(restname);
      }
    }

    return results;
  }

	/**
	 * Method to return a list of restaurants that are in the desired zip code
	 * 
	 * @param zipCode	String the current zip code to search for restaurants
	 * @return			Vector<String> the list all restaurants in the zip code
	 */
  public Vector<String> getRestaurantsByZip(String zipCode) {
    Vector<String> results = new Vector<String>();
    HashMap<String, Restaurant> restaurants = getRestaurants();

    Iterator<String> iterator = (restaurants.keySet()).iterator();
    while(iterator.hasNext()) {
      String restname = iterator.next(); // get key
      Restaurant r = (Restaurant)(restaurants.get(restname)); // get restaurant
      if(r.getZipCode().equals(zipCode)){
        results.addElement(restname);
      }
    }

    return results;
  }

  /**
   * Method to find a restaurant that matches the searchCase, food type, zip code, and user 
   * preferences.
   *  
   * @param searchCase	the type of search case to use
   * @param foodType	the type of food to search for
   * @param zipCode		the zip code the restaurants must be in
   * @param userPrefs	the preference of the user for that search type
   * @return	restaurants that meet all the criteria
   */
  public Vector<String> getRestaurantsByPreferences(String searchCase, String foodType, String zipCode, Vector<String> userPrefs) {
    Vector<String> results = new Vector<String>();
    HashMap<String, Restaurant> restaurants = getRestaurants();
    Iterator<String> iterator = (restaurants.keySet()).iterator();

    while(iterator.hasNext()) {
      String restname = iterator.next().toString(); // get key
      Restaurant r = (Restaurant)(restaurants.get(restname)); // get restaurant
      Preferences restPrefs = r.getPreferences(); // get preferences

      Vector<String> preferences = new Vector<String>();
      
      if (searchCase.equals("cost")) {preferences = restPrefs.getCost();}
      if (searchCase.equals("meal")) {preferences = restPrefs.getMeal();}
      if (searchCase.equals("type")) {preferences = restPrefs.getType();}
      if (searchCase.equals("mood")) {preferences = restPrefs.getMood();}

      for (int i=0; i < userPrefs.size(); i++) {
        for (int j=0; j < preferences.size(); j++) {
          if (userPrefs.elementAt(i).equals(preferences.elementAt(j))) {
            if (r.getFoodType().equals(foodType) && r.getZipCode().equals(zipCode)) {
              results.addElement(restname);
            }
          }
        }
      }
    }

    return results;
  }
  
  // test reading and writing to db files
  public static void main(String[] args) {
    DataBase db = new DataBase();
    db.writeUserDB();
    db.readUserDB();
    System.out.println(db.getUsers());
//    closeDB();
//    db.readRestaurantDB();
//    System.out.println(db.getRestaurants());
  }

}
